package com.zan.service.impl;

import com.zan.dao.OrderItemMapper;
import com.zan.dao.OrdersMapper;
import com.zan.dao.ProductSkuMapper;
import com.zan.dao.ShoppingCartMapper;
import com.zan.entity.*;
import com.zan.service.OrderService;
import com.zan.service.config.RedissonConfig;
import com.zan.utils.PageHelper;
import com.zan.vo.ResStatus;
import com.zan.vo.ResultVo;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;

import java.math.BigDecimal;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private OrdersMapper ordersMapper;

    @Autowired
    private ShoppingCartMapper shoppingCartMapper;

    @Autowired
    private OrderItemMapper orderItemMapper;

    @Autowired
    private ProductSkuMapper productSkuMapper;

    private Logger logger = LoggerFactory.getLogger(OrderServiceImpl.class);

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    /**
     * 添加订单
     */
    @Override
    @Transactional
    public Map<String, String> addOrder(String cids, Orders orders) throws SQLException {
        // 只能解决单机并发问题
        logger.info("add order begin...");
        Map<String, String> map = null;
        //1.根据cids查询到当前订单中关联的购物车记录详情（包括库存）
        List<Integer> cidsList = new ArrayList<>();
        String[] split = cids.split(",");
        for (String s : split) {
            cidsList.add(Integer.parseInt(s));
        }
        // 根据用户在购物车列表中选择的购物车记录的id，查询到对应的购物车记录
        List<ShoppingCartVo> shoppingCartVos = shoppingCartMapper.selectShopCartByCids(cidsList);

        // 从购物车信息中获取到要购买的商品的skuId，以skuId为key写到redis中
        boolean isLock = true;
        /*String[] skuIds = new String[shoppingCartVos.size()]; // ["1", "2", null]
        Map<String, String> values = new HashMap<>();
        for (int i = 0; i < shoppingCartVos.size(); i++) {
            String skuId = shoppingCartVos.get(i).getSkuId(); //订单中可能包含多个商品，每个skuId表示一个商品
            *//**
         * 生成唯一的字符串，帮助他删除的都是自己的锁，而不是别人的锁
         *//*
            String value = UUID.randomUUID().toString();
            Boolean ifAbsent = stringRedisTemplate.boundValueOps(skuId).setIfAbsent(value, 10, TimeUnit.SECONDS);
            if (ifAbsent) {
                skuIds[i] = skuId;
                values.put(skuId, value);
            }
            isLock = isLock && ifAbsent;
        }*/

        String[] skuIds = new String[shoppingCartVos.size()]; // ["1", "2", null]
        Map<String, RLock> locks = new HashMap<>(); // {"1": lock1, "2": lock2, "3": lock3}
        for (int i = 0; i < shoppingCartVos.size(); i++) {
            String skuId = shoppingCartVos.get(i).getSkuId();
            // 构建当前商品的锁
            RLock lock = redissonClient.getLock(skuId);
            // 进行加锁操作 - true 加锁成功
            boolean b = false;
            try {
                b = lock.tryLock(10, 3, TimeUnit.SECONDS);
                if (b) {
                    skuIds[i] = skuId;
                    locks.put(skuId, lock);
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            isLock = isLock && b;
        }

        // 如果isLock为true，表示“加锁”成功
        try {
            if (isLock) {
                //2.校验库存
                /**
                 * 当第一次查询购物车记录之后，在加锁成功之前，可能被其他的并发线程修改库存，因此不能使用第一次的shoppingCartVos
                 * 因此需要重新查询
                 */
                shoppingCartVos = shoppingCartMapper.selectShopCartByCids(cidsList);

                boolean flag = true; //校验结果
                String untitled = "";
                for (ShoppingCartVo shoppingCartVo : shoppingCartVos) {
                    if (Integer.parseInt(shoppingCartVo.getCartNum()) > shoppingCartVo.getStock()) {
                        flag = false;
//                break;
                    }
                    // 获取所有商品的名字, 以,分割，拼接成字符串
                    untitled = untitled + shoppingCartVo.getProductName() + ",";
                }

                if (flag) {
                    logger.info("product stock is OK...");
                    //3.表示库存充足 -- 保存订单
                    //userId [前]
                    //untitled
                    //收货人信息: 姓名、电话、地址 [前]
                    //总价格 [前]
                    //支付方式 [前]
                    //订单创建时间
                    //订单初始状态(待支付 1)
                    orders.setUntitled(untitled);
                    orders.setCreateTime(new Date());
                    orders.setStatus("1");

                    //生成订单编号 UUID
                    String orderId = UUID.randomUUID().toString().replace("-", "");
                    orders.setOrderId(orderId);

                    //保存订单
                    int i = ordersMapper.insert(orders);
                    //4.生成商品快照
                    for (ShoppingCartVo shoppingCartVo : shoppingCartVos) {
                        String itemId = System.currentTimeMillis() + "" + (new Random().nextInt(8999) + 1000);
                        OrderItem orderItem = new OrderItem(
                                itemId,
                                orderId,
                                shoppingCartVo.getProductId(),
                                shoppingCartVo.getProductName(),
                                shoppingCartVo.getProductImg(),
                                shoppingCartVo.getSkuId(),
                                shoppingCartVo.getSkuName(),
                                new BigDecimal(shoppingCartVo.getSellPrice()),
                                Integer.parseInt(shoppingCartVo.getCartNum()),
                                new BigDecimal(Integer.parseInt(shoppingCartVo.getCartNum()) * shoppingCartVo.getSellPrice()),
                                new Date(),
                                new Date(),
                                0);
                        int j = orderItemMapper.insert(orderItem);
                    }
                    //5.扣减库存 -- 更新商品表 根据套餐ID修改套餐库存量
                    for (ShoppingCartVo shoppingCartVo : shoppingCartVos) {
                        //每个套餐都要减少库存
                        String skuId = shoppingCartVo.getSkuId();
                        //新库存
                        int newStock = shoppingCartVo.getStock() - Integer.parseInt(shoppingCartVo.getCartNum());

//                        Example example = new Example(ProductSku.class);
//                        Example.Criteria criteria = example.createCriteria();
//                        criteria.andEqualTo("skuId", skuId);

                        //可以实现，但效果不好，进行了多次数据库操作
//                        ProductSku productSku = productSkuMapper.selectByPrimaryKey(skuId);
//                        productSku.setStock(newStock);
//                        int k = productSkuMapper.updateByExample(productSku, example);

                        ProductSku productSku = new ProductSku();
                        productSku.setSkuId(skuId);
                        productSku.setStock(newStock);
                        int k = productSkuMapper.updateByPrimaryKeySelective(productSku);
                    }
                    //6.删除购物车记录: 当购物车的记录购买成功之后，购物车要对应做删除操作
                    for (int cid : cidsList) {
                        int l = shoppingCartMapper.deleteByPrimaryKey(cid);
                    }

                /*for (int m = 0; m < skuIds.length; m++) {
                    String skuId = skuIds[m];
                    if (skuId != null && !"".equals(skuId)) {
                        String s = stringRedisTemplate.boundValueOps(skuId).get();
                        *//**
                     * 只有当redis的锁UUID和map里面的UUID相等时，才表示这个锁是自己的，而不是别人的
                     *//*
                        if (s != null && s.equals(values.get(skuId))) {
                            *//**
                     * 这里还有个问题，如果在这个时候锁过期了，并且别人加锁成功，那我删除的就是别人的锁，就有问题
                     *//*
                            stringRedisTemplate.delete(skuId);
                        }
                    }
                }*/
                    map = new HashMap<>();
                    map.put("orderId", orderId);
                    map.put("productName", untitled);
                    logger.info("add order finished...");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 释放“锁”
            for (int i = 0; i < skuIds.length; i++) {
                String skuId = skuIds[i];
                if (skuId != null && !"".equals(skuId)) {
                    // 查找到skuId为key的锁，然后解锁
                    locks.get(skuId).unlock();
                }
            }
        }
        return map;
    }

    @Override
    public int updateOrderStatus(String orderId, String status) {
        Orders orders = new Orders();
        orders.setOrderId(orderId);
        orders.setStatus(status);

        int i = ordersMapper.updateByPrimaryKeySelective(orders);

        return i;
    }

    @Override
    public ResultVo getOrderById(String orderId) {

        Orders orders = ordersMapper.selectByPrimaryKey(orderId);

        return new ResultVo(ResStatus.OK.code, "success", orders.getStatus());
    }

    @Override
    @Transactional(isolation = Isolation.SERIALIZABLE) //串行化: 别人干活的时候，你等着，别人干完活，你再干活，即不能一起还原数据
    public void closeOrder(String orderId) {
        synchronized (this) { //this 就表示锁住service对象
            Orders cancelOrder = new Orders();
            cancelOrder.setOrderId(orderId);
            cancelOrder.setStatus("6"); //已关闭
            cancelOrder.setCloseType(1); //关闭类型: 超时未支付
            int i = ordersMapper.updateByPrimaryKeySelective(cancelOrder);
            //2.2.3 还原库存: 先根据当前订单编号查询商品快照(skuid buy_count) --> 修改product_sku表

            Example example1 = new Example(OrderItem.class);
            Example.Criteria criteria1 = example1.createCriteria();
            criteria1.andEqualTo("orderId", orderId);
            List<OrderItem> orderItems = orderItemMapper.selectByExample(example1);
            //还原库存
            for (OrderItem orderItem : orderItems) {
                //修改
                ProductSku productSku = productSkuMapper.selectByPrimaryKey(orderItem.getSkuId());
                productSku.setStock(productSku.getStock() + orderItem.getBuyCounts());
                int j = productSkuMapper.updateByPrimaryKeySelective(productSku);
            }
        }
    }

    @Override
    public ResultVo listOrders(String userId, String status, int pageNum, int limit) {
        //1.分页查询
        int start = (pageNum - 1) * limit;

        List<OrdersVo> ordersVos = ordersMapper.selectOrders(userId, status, start, limit);

        //2.查询总记录数
        Example example = new Example(Orders.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("userId", userId);
        if (status != null && !"".equals(status)) {
            criteria.andEqualTo("status", status);
        }
        int count = ordersMapper.selectCountByExample(example);

        //3.计算总页数
        int pageCount = count % limit == 0 ? count / limit : count / limit + 1;

        //4.封装数据
        PageHelper<OrdersVo> pageHelper = new PageHelper<>(count, pageCount, ordersVos);

        return new ResultVo(ResStatus.OK.code, "success", pageHelper);
    }

    @Override
    public ResultVo listOrders2(String userId, String status, int pageNum, int limit) {
        //1.分页查询
        int start = (pageNum - 1) * limit;

        List<OrdersVo> ordersVos = ordersMapper.selectOrders2(userId, status, start, limit);

        //2.查询总记录数
        Example example = new Example(Orders.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("userId", userId);
        if (status != null && !"".equals(status)) {
            criteria.andEqualTo("status", status);
        }
        int count = ordersMapper.selectCountByExample(example);

        //3.计算总页数
        int pageCount = count % limit == 0 ? count / limit : count / limit + 1;

        //4.封装数据
        PageHelper<OrdersVo> pageHelper = new PageHelper<>(count, pageCount, ordersVos);

        return new ResultVo(ResStatus.OK.code, "success", pageHelper);
    }
}
